package com.specter.utils;

import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

import org.apache.commons.lang3.reflect.FieldUtils;
import org.springframework.beans.FatalBeanException;
import org.springframework.util.Assert;

/**
 * Note:使用spring的bean对拷工具。
 * 
 * @author Liang.Wang
 * @version Mar 24, 2011 1:32:34 PM
 */
public class BeanUtils extends org.springframework.beans.BeanUtils {

	/**
	 * 将source对象的内容copy到target对象中，若俩对象中有null则不做操作
	 * 
	 * @param source 源头对象
	 * @param target 目标对象
	 */
	public static void copyProperties(Object source, Object target) {
		if (source != null && target != null) {
			org.springframework.beans.BeanUtils.copyProperties(source, target);
		}

	}

	/**
	 * 比较新旧Bean中的差异字段。俩Bean都不能为null。<br/>
	 * 返回 <字段名,【旧字段值，新字段值】>，无字段改变则返回null
	 * 
	 * @param oldBean
	 * @param newBean
	 * @return <字段名,【旧字段值，新字段值】>
	 */
	public static Map<String, String[]> compareProperties(Object oldBean, Object newBean) {

		Assert.notNull(oldBean, "oldBean must not be null");
		Assert.notNull(newBean, "newBean must not be null");

		Map<String, String[]> map = new HashMap<>();
		int count = 0;

		Class<?> actualEditable = newBean.getClass();
		PropertyDescriptor[] newBeanPds = getPropertyDescriptors(actualEditable);

		for (int i = 0; i < newBeanPds.length; i++) {
			PropertyDescriptor newBeanPd = newBeanPds[i];
			PropertyDescriptor oldBeanPd = getPropertyDescriptor(oldBean.getClass(), newBeanPd.getName());

			if (oldBeanPd != null && oldBeanPd.getReadMethod() != null) {
				String[] v = new String[2];
				try {
					// 取老bean中的值
					Method oldRead = oldBeanPd.getReadMethod();
					Object oldValue = oldRead.invoke(oldBean);
					v[0] = Objects.toString(oldValue, "");

					// 取新bean中的值
					Method newRead = newBeanPd.getReadMethod();

					Object newValue = newRead.invoke(newBean);
					v[1] = Objects.toString(newValue, "");

					if (!v[0].equals(v[1])) {
						map.put(oldBeanPd.getName(), v);
						count++;
					}

				} catch (Exception ex) {
					throw new FatalBeanException("Could not compare the properties", ex);
				}
			}

		}
		if (count == 0) {
			return null;
		}
		return map;
	}

	/**
	 * 将source对象中不为null属性copy到target对象中响应的属性中
	 * 
	 * @param source 源头对象
	 * @param target 目标对象
	 */
	public static void copyNotNullProperties(Object source, Object target) {

		Assert.notNull(source, "Source must not be null");
		Assert.notNull(target, "Target must not be null");

		Class<?> actualEditable = target.getClass();

		PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable);

		for (int i = 0; i < targetPds.length; i++) {
			PropertyDescriptor targetPd = targetPds[i];
			if (targetPd.getWriteMethod() != null) {
				PropertyDescriptor sourcePd = getPropertyDescriptor(source.getClass(), targetPd.getName());
				if (sourcePd != null && sourcePd.getReadMethod() != null) {
					try {
						Method readMethod = sourcePd.getReadMethod();
						Object value = readMethod.invoke(source);
						if (value != null) {// 不为null时才进行赋值，""还是能赋值
							Method writeMethod = targetPd.getWriteMethod();
							writeMethod.invoke(target, value);
						}
					} catch (Exception ex) {
						throw new FatalBeanException("Could not copy properties from source to target", ex);
					}
				}
			}
		}
	}

	public static Object get(Object obj, String fieldName) {
		Object value = null;
		if (obj instanceof Map<?, ?> map) {
			value = map.get(fieldName);
		} else {
			try {
				value = FieldUtils.readField(obj, fieldName, true);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		return value;
	}

	@SuppressWarnings("unchecked")
	public static void set(Object obj, String fieldName, Object value) {
		if (obj instanceof Map) {
			((Map<String, Object>) obj).put(fieldName, value);
		} else {
			try {
				FieldUtils.writeField(obj, fieldName, value);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}

	/**
	 * 将实体bean对象转换成Map对象,若为Map类型直接返回
	 * 
	 * @param obj
	 * @return
	 * @throws Exception
	 */
	@SuppressWarnings("unchecked")
	public static Map<String, Object> obj2Map(Object obj) {
		Assert.notNull(obj, "Target must not be null");
		Map<String, Object> map = new HashMap<>();
		if (obj instanceof Map) {
			return (Map<String, Object>) obj;
		}
		try {
			BeanInfo beanInfo = Introspector.getBeanInfo(obj.getClass());
			PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
			for (PropertyDescriptor property : propertyDescriptors) {
				String key = property.getName();
				if (!key.equals("class")) {// 过滤掉getClass方法
					Method getter = property.getReadMethod();
					Object value = getter.invoke(obj);
					map.put(key, value);
				}
			}
		} catch (Exception e) {
			System.out.println("Obj转Map异常:" + e.getMessage());
		}

		return map;
	}

	// 将Map集合转成实体
	public static <T> T map2Obj(Map<?, ?> map, Class<T> clazz) {
		if (map == null) {
			return null;
		}
		T obj = null;
		try {
			obj = clazz.getDeclaredConstructor().newInstance();
			Field[] fields = obj.getClass().getDeclaredFields();
			for (Field field : fields) {
				int mod = field.getModifiers();
				if (Modifier.isStatic(mod) || Modifier.isFinal(mod)) {
					continue;
				}
				field.setAccessible(true);
				field.set(obj, map.get(field.getName()));
				// FieldUtils.writeField(obj, field.getName(), map.get(field.getName()), true);
			}
		} catch (Exception e) {
			System.out.println("Map转Obj异常:" + e.getMessage());
		}
		return obj;
	}
}
